<!DOCTYPE html>
<html lang="zh-CN" dir="ltr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>登录页面 | 优医问诊H5</title>
    <meta name="description" content="最新Vue3技术栈,Vue3,TS,Pinia,Vant,在线问诊项目,H5">
    <link rel="preload stylesheet" href="/hm-docs/assets/style.f60a3f91.css" as="style">
    <script type="module" src="/hm-docs/assets/app.5c44d29a.js"></script>
    <link rel="preload" href="/hm-docs/assets/inter-roman-latin.2ed14f66.woff2" as="font" type="font/woff2" crossorigin="">
  <link rel="modulepreload" href="/hm-docs/assets/chunks/framework.956eca13.js">
  <link rel="modulepreload" href="/hm-docs/assets/chunks/theme.bb7bd755.js">
  <link rel="modulepreload" href="/hm-docs/assets/geek-park_02-登录页面.md.6ab1a2cf.lean.js">
  <link rel="icon" type="image/svg+xml" href="./logo2.png">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css">
  <script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@4.0/dist/fancybox.umd.js"></script>
  <script id="check-dark-light">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
  </head>
  <body>
    <div id="app"><div class="Layout" data-v-99cf8a88><!--[--><!--]--><!--[--><span tabindex="-1" data-v-ae3e3f51></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-ae3e3f51> Skip to content </a><!--]--><!----><header class="VPNav" data-v-99cf8a88 data-v-7e5bc4a5><div class="VPNavBar" data-v-7e5bc4a5 data-v-381568bf><div class="container" data-v-381568bf><div class="title" data-v-381568bf><div class="VPNavBarTitle" data-v-381568bf data-v-305adf00><a class="title" href="/hm-docs/" data-v-305adf00><!--[--><!--]--><!--[--><img class="VPImage logo" src="/hm-docs/logo2.png" alt data-v-6db2186b><!--]--><!--[-->优医问诊H5<!--]--><!--[--><!--]--></a></div></div><div class="content" data-v-381568bf><div class="curtain" data-v-381568bf></div><div class="content-body" data-v-381568bf><!--[--><!--]--><div class="VPNavBarSearch search" style="--vp-meta-key:&#39;Meta&#39;;" data-v-381568bf><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg class="DocSearch-Search-Icon" width="20" height="20" viewBox="0 0 20 20" aria-label="search icon"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-381568bf data-v-7f418b0f><span id="main-nav-aria-label" class="visually-hidden" data-v-7f418b0f>Main Navigation</span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/vue2/base.html" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->vue2<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/vue2/vuex.html" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->vuex<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/interview/%E9%9D%A2%E7%BB%8FH5%E7%AB%AF01.html" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->面经<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/hr/Day01-%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D%E5%8F%8A%E5%88%9D%E5%A7%8B%E5%8C%96-%E7%99%BB%E5%BD%95%E9%A1%B5%E9%9D%A2.html" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->人资<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/hmzs/%E5%89%8D%E5%8F%B0%E5%8F%AF%E8%A7%86%E5%8C%96/01-%E9%A1%B9%E7%9B%AE%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.html" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->黑马智数<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/mini-rabbit/" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->小兔鲜儿<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/ts/" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->TypeScript<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/vue3/" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->Vue3<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/pinia/" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->Pinia<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/project/" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->优医问诊<!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/hm-docs/other-resource/" tabindex="0" data-v-7f418b0f data-v-5e623618><!--[-->其它<!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-381568bf data-v-f6a63727><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-f6a63727 data-v-82b282f1 data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-82b282f1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-82b282f1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-381568bf data-v-0394ad82 data-v-7bc22406><!--[--><a class="VPSocialLink" href="https://gitee.com/luckybo0027" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-36371990><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-381568bf data-v-40855f84 data-v-764effdf><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-764effdf><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="icon" data-v-764effdf><circle cx="12" cy="12" r="2"></circle><circle cx="19" cy="12" r="2"></circle><circle cx="5" cy="12" r="2"></circle></svg></button><div class="menu" data-v-764effdf><div class="VPMenu" data-v-764effdf data-v-e7ea1737><!----><!--[--><!--[--><!----><div class="group" data-v-40855f84><div class="item appearance" data-v-40855f84><p class="label" data-v-40855f84>Appearance</p><div class="appearance-action" data-v-40855f84><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-40855f84 data-v-82b282f1 data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-82b282f1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-82b282f1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div></div></div><div class="group" data-v-40855f84><div class="item social-links" data-v-40855f84><div class="VPSocialLinks social-links-list" data-v-40855f84 data-v-7bc22406><!--[--><a class="VPSocialLink" href="https://gitee.com/luckybo0027" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-36371990><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-381568bf data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><!----></header><!----><!----><div class="VPContent" id="VPContent" data-v-99cf8a88 data-v-97b5189c><div class="VPDoc has-aside" data-v-97b5189c data-v-e9631fd0><!--[--><!--]--><div class="container" data-v-e9631fd0><div class="aside" data-v-e9631fd0><div class="aside-curtain" data-v-e9631fd0></div><div class="aside-container" data-v-e9631fd0><div class="aside-content" data-v-e9631fd0><div class="VPDocAside" data-v-e9631fd0 data-v-3f215769><!--[--><!--]--><!--[--><!--]--><div class="VPDocAsideOutline" data-v-3f215769 data-v-ff0f39c8><div class="content" data-v-ff0f39c8><div class="outline-marker" data-v-ff0f39c8></div><div class="outline-title" data-v-ff0f39c8>On this page</div><nav aria-labelledby="doc-outline-aria-label" data-v-ff0f39c8><span class="visually-hidden" id="doc-outline-aria-label" data-v-ff0f39c8> Table of Contents for current page </span><ul class="root" data-v-ff0f39c8 data-v-b0ff2abe><!--[--><!--]--></ul></nav></div></div><!--[--><!--]--><div class="spacer" data-v-3f215769></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-e9631fd0><div class="content-container" data-v-e9631fd0><!--[--><!--]--><!----><main class="main" data-v-e9631fd0><div style="position:relative;" class="vp-doc _hm-docs_geek-park_02-%E7%99%BB%E5%BD%95%E9%A1%B5%E9%9D%A2" data-v-e9631fd0><div><h1 id="登录页面" tabindex="-1">登录页面 <a class="header-anchor" href="#登录页面" aria-label="Permalink to &quot;登录页面&quot;">​</a></h1><p><img src="/hm-docs/assets/image-20211118224213236.910edd77.png" alt="image-20211118224213236" data-fancybox="gallery"></p><h2 id="表单静态结构" tabindex="-1">表单静态结构 <a class="header-anchor" href="#表单静态结构" aria-label="Permalink to &quot;表单静态结构&quot;">​</a></h2><ul><li>结构 <code>src\pages\Login\index.tsx</code> -- 直接复制</li></ul><div class="language-jsx line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">jsx</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> NavBar </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">@/components/NavBar</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> styles </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">./index.module.scss</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">Login</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> (</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;">styles</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">root</span><span style="color:#89DDFF;">}&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;</span><span style="color:#FFCB6B;">NavBar</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">登录</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#FFCB6B;">NavBar</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">content</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">{</span><span style="color:#676E95;font-style:italic;">/* 标题 */</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">短信登录</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">form</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">{</span><span style="color:#676E95;font-style:italic;">/* 手机号输入框 */</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">input-item</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">input-box</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">              </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">input</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">autoFocus</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">input</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">placeholder</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">请输入手机号</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">autoComplete</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">off</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">text</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">name</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">mobile</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">maxLength</span><span style="color:#89DDFF;">={</span><span style="color:#F78C6C;">11</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">              /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">              </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">validate</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">手机号格式错误</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">{</span><span style="color:#676E95;font-style:italic;">/* 短信验证码输入框 */</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">input-item</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">input-box</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">              </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">input</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">input</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">placeholder</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">请输入验证码</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">autoComplete</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">off</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">text</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">name</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">code</span><span style="color:#89DDFF;">&quot;</span></span>
<span class="line"><span style="color:#89DDFF;">                </span><span style="color:#C792EA;">maxLength</span><span style="color:#89DDFF;">={</span><span style="color:#F78C6C;">6</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">              /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">              </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">validate</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">验证码格式错误</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">              </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">extra</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">获取验证码</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">{</span><span style="color:#676E95;font-style:italic;">/* 登录按钮 */</span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">type</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">submit</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">login-btn</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">disabled</span><span style="color:#89DDFF;">={</span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">}&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">            登录</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">form</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#F07178;">  )</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br></div></div><ul><li>样式 <code>src\pages\Login\index.module.scss</code> -- 直接复制</li></ul><div class="language-scss line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">scss</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">@import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">@/assets/styles/hairline.scss</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">root</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;">global {</span></span>
<span class="line"><span style="color:#A6ACCD;">    .iconfanhui {</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">font-size</span><span style="color:#A6ACCD;">: </span><span style="color:#F78C6C;">20px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">content</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">padding</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">32px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#FFCB6B;">h3</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">padding</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">30px</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">font-size</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">24px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">input-item</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">position</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> relative</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#FFCB6B;">&amp;</span><span style="color:#89DDFF;">:</span><span style="color:#C792EA;">first-child</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#B2CCD6;">margin-bottom</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">17px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">input-box</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#B2CCD6;">position</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> relative</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;font-style:italic;">@include</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">hairline</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">bottom, </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">efefef</span><span style="color:#89DDFF;">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">input</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">width</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">100%</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">height</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">58px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">padding</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">font-size</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">16px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#FFCB6B;">&amp;</span><span style="color:#89DDFF;">::</span><span style="color:#C792EA;">placeholder</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">              </span><span style="color:#B2CCD6;">color</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">a5a6ab</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">extra</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">position</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> absolute</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">right</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">top</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">50%</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">margin-top</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">-8px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">color</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">999</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">validate</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">position</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> absolute</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">color</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">ee0a24</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#B2CCD6;">font-size</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">12px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">login-btn</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">width</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">100%</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">height</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">50px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">margin-top</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">38px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">border-radius</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">8px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">border</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">color</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">fff</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">background</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">linear-gradient</span><span style="color:#89DDFF;">(</span><span style="color:#F78C6C;">315deg</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">fe4f4f</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">fc6627</span><span style="color:#89DDFF;">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#676E95;font-style:italic;">// 按钮禁用效果</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#FFCB6B;">&amp;</span><span style="color:#89DDFF;">[</span><span style="color:#C792EA;">disabled</span><span style="color:#89DDFF;">]</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#B2CCD6;">background</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">linear-gradient</span><span style="color:#89DDFF;">(</span><span style="color:#F78C6C;">315deg</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">ff9999</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">ffa179</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br><span class="line-number">62</span><br><span class="line-number">63</span><br><span class="line-number">64</span><br><span class="line-number">65</span><br><span class="line-number">66</span><br><span class="line-number">67</span><br><span class="line-number">68</span><br><span class="line-number">69</span><br><span class="line-number">70</span><br><span class="line-number">71</span><br></div></div><h2 id="input-组件封装" tabindex="-1">Input 组件封装 <a class="header-anchor" href="#input-组件封装" aria-label="Permalink to &quot;Input 组件封装&quot;">​</a></h2><blockquote><p>目标：将原生的 input 标签进行封装，使得该组件可在 input 右侧放置额外内容元素</p></blockquote><img src="/hm-docs/assets/image-20210831213942878.2fa09d02.png" alt="image-20210831213942878" style="zoom:50%;"><h3 id="基础封装" tabindex="-1">基础封装 <a class="header-anchor" href="#基础封装" aria-label="Permalink to &quot;基础封装&quot;">​</a></h3><p>实现思路：</p><ul><li>左右布局：左侧<code>&lt;input&gt;</code> ，右侧是一个可自定义的内容区域</li><li>将封装的组件传入的属性，全部传递到 <code>&lt;input&gt;</code> 标签上，使得能充分利用原标签的功能</li></ul><p><strong>操作步骤</strong></p><ol><li>创建组件： <code>src\components\Input\index.tsx</code>，然后编写组件代码：</li></ol><div class="language-jsx line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">jsx</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">ReactNode</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">react</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> styles </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./index.module.scss</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> classNames </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">classnames</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C792EA;">interface</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">IInputProps</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">extends</span><span style="color:#A6ACCD;">  </span><span style="color:#FFCB6B;">React</span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">DetailedHTMLProps</span><span style="color:#89DDFF;">&lt;</span><span style="color:#FFCB6B;">React</span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">InputHTMLAttributes</span><span style="color:#89DDFF;">&lt;</span><span style="color:#FFCB6B;">HTMLInputElement</span><span style="color:#89DDFF;">&gt;,</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">HTMLInputElement</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">extra</span><span style="color:#89DDFF;">?:</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">ReactNode</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">errorMsg</span><span style="color:#89DDFF;">?:</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">string</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">Input</span><span style="color:#89DDFF;">({</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">placeholder</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">className</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">extra</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">errorMsg</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;font-style:italic;">rest</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">}:</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">IInputProps</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> (</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;">styles</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">root</span><span style="color:#89DDFF;">}&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">input</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">placeholder</span><span style="color:#89DDFF;">={</span><span style="color:#A6ACCD;">placeholder</span><span style="color:#89DDFF;">} </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">={</span><span style="color:#82AAFF;">classNames</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">input</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> className)</span><span style="color:#89DDFF;">} {...</span><span style="color:#A6ACCD;">rest</span><span style="color:#89DDFF;">} /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">extra </span><span style="color:#89DDFF;">&amp;&amp;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">extra</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">extra</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">errorMsg </span><span style="color:#89DDFF;">&amp;&amp;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">className</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">validate</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">&gt;{</span><span style="color:#A6ACCD;">errorMsg</span><span style="color:#89DDFF;">}&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#F07178;">  )</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div><ol start="2"><li>创建组件样式：<code>src\components\Input\index.module.scss</code></li></ol><div class="language-scss line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">scss</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">@import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">@/assets/styles/hairline.scss</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">root</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#B2CCD6;">position</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> relative</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;font-style:italic;">@include</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">hairline</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">bottom, </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">efefef</span><span style="color:#89DDFF;">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;">global {</span></span>
<span class="line"><span style="color:#A6ACCD;">    .input {</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">width</span><span style="color:#A6ACCD;">: </span><span style="color:#F78C6C;">100%</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">height</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">58px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">padding</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">font-size</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">16px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#FFCB6B;">&amp;</span><span style="color:#89DDFF;">::</span><span style="color:#C792EA;">placeholder</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#B2CCD6;">color</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">a5a6ab</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">extra</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">position</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> absolute</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">right</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">top</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">50%</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">margin-top</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">-8px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">color</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">999</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">.</span><span style="color:#FFCB6B;">validate</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">position</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> absolute</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">color</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">#</span><span style="color:#A6ACCD;">ee0a24</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#B2CCD6;">font-size</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">12px</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br></div></div><ol start="3"><li>页面中使用 <code>src\pages\Login\index.tsx</code></li></ol><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;form&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  {/* 手机号输入框 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div className=&#39;input-item&#39;&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    &lt;Input placeholder=&#39;请输入手机号&#39; errorMsg=&#39;错误信息&#39; extra={&lt;span&gt;额外内容&lt;/span&gt;} /&gt;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><h3 id="props-高级写法" tabindex="-1">Props 高级写法 <a class="header-anchor" href="#props-高级写法" aria-label="Permalink to &quot;Props 高级写法&quot;">​</a></h3><blockquote><p>目标：通过 interface 继承 React 内置的 Props，简化 Input 组件属性定义。</p></blockquote><p>修改文件：<code>src\components\Input\index.tsx</code></p><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">import { ReactNode } from &#39;react&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import styles from &#39;./index.module.scss&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import classNames from &#39;classnames&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">// Props 高级写法，继承所有 input 属性</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">interface Props</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  extends React.DetailedHTMLProps&lt;React.InputHTMLAttributes&lt;HTMLInputElement&gt;, HTMLInputElement&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">type Props = {</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">  [x: string]: any</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">  placeholder?: string</span></span>
<span class="line"><span style="color:#A6ACCD;">  className?: string</span></span>
<span class="line"><span style="color:#A6ACCD;">  extra?: ReactNode</span></span>
<span class="line"><span style="color:#A6ACCD;">  errorMsg?: string</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">export default function Input({ placeholder, className, extra, errorMsg, ...rest }: Props) {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">export default function Input({ className, extra, errorMsg, ...rest }: Props) {</span></span>
<span class="line"><span style="color:#A6ACCD;">  return (</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;div className={styles.root}&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">      &lt;input placeholder={placeholder} className={classNames(&#39;input&#39;, className)} {...rest} /&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      &lt;input className={classNames(&#39;input&#39;, className)} {...rest} /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;div className=&#39;extra&#39;&gt; {extra} &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;div className=&#39;validate&#39;&gt;{errorMsg}&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  )</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br></div></div><p>修改文件：<code>src\pages\Login\index.tsx</code></p><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;form&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">          {/* 手机号输入框 */}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">            &lt;Input</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              autoFocus</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              className=&#39;input&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              placeholder=&#39;请输入手机号&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              autoComplete=&#39;off&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              type=&#39;text&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              name=&#39;mobile&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              maxLength={11}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              errorMsg=&#39;手机号格式错误&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">            /&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">          {/* 短信验证码输入框 */}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">          &lt;Input</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              className=&#39;input&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              placeholder=&#39;请输入验证码&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              autoComplete=&#39;off&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              type=&#39;text&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              name=&#39;code&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              maxLength={6}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              errorMsg=&#39;验证码格式错误&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              extra={&lt;span&gt;获取验证码&lt;/span&gt;}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">            /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">          {/* 登录按钮 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">          &lt;button type=&#39;submit&#39; className=&#39;login-btn&#39; disabled&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">            登录</span></span>
<span class="line"><span style="color:#A6ACCD;">          &lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;/form&gt;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br></div></div><h2 id="表单数据绑定" tabindex="-1">表单数据绑定 <a class="header-anchor" href="#表单数据绑定" aria-label="Permalink to &quot;表单数据绑定&quot;">​</a></h2><blockquote><p>目标：为登录表单中的输入组件进行数据绑定，收集表单数据。</p><p>PS：大家课后可新建项目分支，尝试用 <code>antd-mobile</code> 组件库里的表单组件来实现。</p></blockquote><p>实现思路：</p><ul><li>使用 <code>formik</code> 库进行通用表单的数据绑定</li><li>官方文档：<a href="https://formik.org/docs/api/useFormik" target="_blank" rel="noreferrer">https://formik.org/docs/api/useFormik</a></li></ul><p><strong>操作步骤</strong></p><ol><li>安装 <code>formik</code></li></ol><div class="language-bash line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">yarn</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">add</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">formik</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><ol start="2"><li>使用 <code>formik</code> 库中提供的 Hook 函数创建 <code>formik</code> 表单对象</li></ol><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">import Input from &#39;@/components/Input&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import NavBar from &#39;@/components/NavBar&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">import { useFormik } from &#39;formik&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import styles from &#39;./index.module.scss&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">export default function Login() {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  const formik = useFormik({</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    initialValues: {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      mobile: &#39;13911111111&#39;,</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      code: &#39;123456&#39;,</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    },</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    onSubmit: (values) =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      console.log(values)</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    },</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  })</span></span>
<span class="line"><span style="color:#A6ACCD;">  return (</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;div className={styles.root}&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;NavBar&gt;登录&lt;/NavBar&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;div className=&#39;content&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        {/* 标题 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;h3&gt;短信登录&lt;/h3&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">        &lt;form onSubmit={formik.handleSubmit}&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">          {/* 手机号输入框 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">         &lt;Input</span></span>
<span class="line"><span style="color:#A6ACCD;">              autoFocus</span></span>
<span class="line"><span style="color:#A6ACCD;">              placeholder=&#39;请输入手机号&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              autoComplete=&#39;off&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              type=&#39;text&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              name=&#39;mobile&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              maxLength={11}</span></span>
<span class="line"><span style="color:#A6ACCD;">              errorMsg=&#39;手机号格式错误&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              value={formik.values.mobile}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              onChange={formik.handleChange}</span></span>
<span class="line"><span style="color:#A6ACCD;">            /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">          {/* 短信验证码输入框 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">          &lt;Input</span></span>
<span class="line"><span style="color:#A6ACCD;">          	  placeholder=&#39;请输入验证码&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              autoComplete=&#39;off&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              type=&#39;text&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              name=&#39;code&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              maxLength={6}</span></span>
<span class="line"><span style="color:#A6ACCD;">              errorMsg=&#39;验证码格式错误&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              extra={&lt;span&gt;获取验证码&lt;/span&gt;}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              value={formik.values.code}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              onChange={formik.handleChange}</span></span>
<span class="line"><span style="color:#A6ACCD;">            /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">          {/* 登录按钮 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">          &lt;button type=&#39;submit&#39; className=&#39;login-btn&#39; disabled={false}&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">            登录</span></span>
<span class="line"><span style="color:#A6ACCD;">          &lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;/form&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  )</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br></div></div><ol start="3"><li>点击登录按钮测试 <strong>获取表单数据</strong> 是否成功</li></ol><h2 id="表单数据验证" tabindex="-1">表单数据验证 <a class="header-anchor" href="#表单数据验证" aria-label="Permalink to &quot;表单数据验证&quot;">​</a></h2><blockquote><p>目标：对表单数据进行校验，校验不通过禁用按钮。</p><p>参考文档：<a href="https://formik.org/docs/tutorial#validation" target="_blank" rel="noreferrer">https://formik.org/docs/tutorial#validation</a></p></blockquote><img src="/hm-docs/assets/image-20210901091137594.b7932885.png" alt="image-20210901091137594" style="zoom:50%;"><ul><li>给 <code>useFormik</code> 提供 <code>validate</code> 函数进行校验</li></ul><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">type FormValues = {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  mobile: string</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  code: string</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">export default function Login() {</span></span>
<span class="line"><span style="color:#A6ACCD;">  const formik = useFormik({</span></span>
<span class="line"><span style="color:#A6ACCD;">    initialValues: {</span></span>
<span class="line"><span style="color:#A6ACCD;">      mobile: &#39;13911111111&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">      code: &#39;123456&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">    } as FormValues,</span></span>
<span class="line"><span style="color:#A6ACCD;">    onSubmit: (values) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">      console.log(values)</span></span>
<span class="line"><span style="color:#A6ACCD;">    },</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    validate(values) {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      // 添加 TS 格式断言</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      const errors = {} as FormValues</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      if (!/^1[3-9]\d{9}$/.test(values.mobile)) {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">        errors.mobile = &#39;手机号格式错误&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      }</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      if (!/^\d{6}$/.test(values.code)) {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">        errors.code = &#39;验证码格式错误&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      }</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      return errors</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    },</span></span>
<span class="line"><span style="color:#A6ACCD;">  })</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br></div></div><ul><li>需要给每一个表单元素绑定一个事件 <code>onBlur</code> , 目的是为了区分那些输入框是被点击过的</li></ul><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">{/* 手机号输入框 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;div className=&#39;input-item&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Input</span></span>
<span class="line"><span style="color:#A6ACCD;">   ....省略</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    errorMsg={formik.touched.mobile ? formik.errors.mobile : &#39;&#39;}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    onBlur={formik.handleBlur}</span></span>
<span class="line"><span style="color:#A6ACCD;">    value={formik.values.mobile}</span></span>
<span class="line"><span style="color:#A6ACCD;">    onChange={formik.handleChange}</span></span>
<span class="line"><span style="color:#A6ACCD;">  /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">{/* 短信验证码输入框 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;div className=&#39;input-item&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Input</span></span>
<span class="line"><span style="color:#A6ACCD;">    ....省略</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    errorMsg={formik.touched.code ? formik.errors.code : &#39;&#39;}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    onBlur={formik.handleBlur}</span></span>
<span class="line"><span style="color:#A6ACCD;">    extra={&lt;span&gt;获取验证码&lt;/span&gt;}</span></span>
<span class="line"><span style="color:#A6ACCD;">    value={formik.values.code}</span></span>
<span class="line"><span style="color:#A6ACCD;">    onChange={formik.handleChange}</span></span>
<span class="line"><span style="color:#A6ACCD;">  /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">{/* 登录按钮 */}</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">&lt;button type=&#39;submit&#39; className=&#39;login-btn&#39; disabled={false}&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">&lt;button type=&#39;submit&#39; className=&#39;login-btn&#39; disabled={!formik.isValid}&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  登录</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/button&gt;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br></div></div><p><strong>用户体验升级</strong></p><ul><li>表单还没开始填写的表单，不需要展示错误信息。</li></ul><h2 id="yup-校验库补充" tabindex="-1">Yup 校验库补充 <a class="header-anchor" href="#yup-校验库补充" aria-label="Permalink to &quot;Yup 校验库补充&quot;">​</a></h2><blockquote><p>目标：Yup 可适用于更复杂的表单格式校验，如校验是否为数字，是否为空，是否为某个内置格式。</p></blockquote><p><strong>操作步骤</strong></p><ol><li>安装 <code>yup</code></li></ol><div class="language-bash line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">yarn</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">add</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">yup</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><ol start="2"><li>在创建 <code>formik</code> 表单对象时，添加表单验证相关参数</li></ol><div class="language-jsx line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">jsx</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">// 有些第三方包，没有设置export default，下方相当于把所有的export 合并为一个对象Yup</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">*</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">as</span><span style="color:#A6ACCD;"> Yup </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">yup</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><ol start="3"><li>改造成 Yup 校验</li></ol><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">const formik = useFormik({</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 表单初始值</span></span>
<span class="line"><span style="color:#A6ACCD;">    initialValues: {</span></span>
<span class="line"><span style="color:#A6ACCD;">      mobile: &#39;&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">      code: &#39;&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">    } as FormValues,</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 提交表单</span></span>
<span class="line"><span style="color:#A6ACCD;">    onSubmit: (values) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">      console.log(&#39;当前表单的数据为&#39;, values)</span></span>
<span class="line"><span style="color:#A6ACCD;">    },</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">    // 校验表单数据</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">    validate: (values) =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">      // 通过 TS 的断言指定格式</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">      const error = {} as FormValues</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">      if (!/^1[3-9]\d{9}$/.test(values.mobile)) error.mobile = &#39;手机号格式错误&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">      if (!/^\d{6}$/.test(values.code)) error.code = &#39;验证码格式错误&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">      // 返回错误对象</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">      return error</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">    },</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // Yup 表单校验</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    validationSchema: Yup.object().shape({</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      // 手机号校验</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      mobile: Yup.string().required(&#39;必填项&#39;).matches(/^1[3-9]\d{9}$/, &#39;手机号格式错误&#39;),</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      // 验证码校验</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      code: Yup.string().required(&#39;必填项&#39;).matches(/^\d{6}$/, &#39;验证码格式错误&#39;),</span></span>
<span class="line"><span style="color:#A6ACCD;">    }),</span></span>
<span class="line"><span style="color:#A6ACCD;">  })</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br></div></div><h1 id="网络请求" tabindex="-1">网络请求 <a class="header-anchor" href="#网络请求" aria-label="Permalink to &quot;网络请求&quot;">​</a></h1><h2 id="axios-初步封装" tabindex="-1"><code>axios</code> 初步封装 <a class="header-anchor" href="#axios-初步封装" aria-label="Permalink to &quot;`axios` 初步封装&quot;">​</a></h2><blockquote><p>目标：将 <code>axios</code> 封装成公用的网络请求模块，方便后续调用后端接口</p></blockquote><p><strong>操作步骤</strong></p><ol><li>安装 <code>axios</code></li></ol><div class="language-bash line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">yarn</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">add</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">axios</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><ol start="2"><li>创建 <code>src\utils\request.ts</code>，并编写如下代码</li></ol><div class="language-ts line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> axios </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">axios</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 请求基地址</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> baseURL </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">http://geek.itheima.net</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 创建 axios 实例</span></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> request </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> axios</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">create</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">baseURL</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> baseURL</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">timeout</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">5000</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 请求拦截器</span></span>
<span class="line"><span style="color:#A6ACCD;">request</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">interceptors</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">request</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">use</span><span style="color:#A6ACCD;">(</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">config</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">config</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">error</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#FFCB6B;">Promise</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">reject</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">error</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 响应拦截器</span></span>
<span class="line"><span style="color:#A6ACCD;">request</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">interceptors</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">response</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">use</span><span style="color:#A6ACCD;">(</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">response</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">response</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">data</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">error</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#FFCB6B;">Promise</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">reject</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">error</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 导出 axios 实例</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> request</span><span style="color:#89DDFF;">;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br></div></div><h2 id="发送手机验证码" tabindex="-1">发送手机验证码 <a class="header-anchor" href="#发送手机验证码" aria-label="Permalink to &quot;发送手机验证码&quot;">​</a></h2><blockquote><p>目标：点击登录界面中的发送验证码按钮，调用后端接口进行验证码的发送</p></blockquote><img src="/hm-docs/assets/image-20210901092838694.c822841f.png" alt="image-20210901092838694" style="zoom:50%;"><p>实现思路：</p><ul><li>新增 <code>AppDispatch</code> 和 <code>RootState</code> 类型注解并导出备用</li><li>实现一个 <code>action</code> 函数，请求发送验证码后端接口</li><li>在验证码的 <code>Input</code> 组件的 <code>extra</code> 监听函数中调用 <code>action</code></li></ul><p><strong>操作步骤</strong></p><ol><li><code>src\store\index.ts</code> 中新增 <code>AppDispatch</code> 和 <code>RootState</code> 类型注解并导出备用</li></ol><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">export type AppDispatch = typeof store.dispatch</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">export type RootState = ReturnType&lt;typeof store.getState&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">export default store</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><ol start="2"><li>新建 <code>src\store\action\user.ts</code>，并实现一个 Action 函数</li></ol><div class="language-js line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> http </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">@/utils/http</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">AppDispatch</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">..</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * 发送短信验证码</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">mobile</span><span style="color:#676E95;font-style:italic;"> 手机号</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">returns</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> sendCodes </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">mobile</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">string</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">async</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">dispatch</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#FFCB6B;">AppDispatch</span><span style="color:#89DDFF;">)</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#C792EA;">const</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">res</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;font-style:italic;">await</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">http</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">get</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">`</span><span style="color:#C3E88D;">/v1_0/sms/codes/</span><span style="color:#89DDFF;">${</span><span style="color:#A6ACCD;">mobile</span><span style="color:#89DDFF;">}`</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">res</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">};</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><ol start="2"><li>为验证码输入框组件添加 <code>handleSendCodes</code> 事件监听</li></ol><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">import { sendCodes } from &#39;@/store/action/user&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">import { useDispatch } from &#39;react-redux&#39;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">import { Toast } from &#39;antd-mobile&#39;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">export default function Login() {</span></span>
<span class="line"><span style="color:#A6ACCD;">  ...代码省略</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  const dispatch = useDispatch()</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  const handleSendCodes = () =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // 校验错误</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    if (formik.errors.mobile) return Toast.show({ content: formik.errors.mobile })</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // 调用发送验证码</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    dispatch(sendCodes(formik.values.mobile))</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">  return (</span></span>
<span class="line"><span style="color:#A6ACCD;">  ...代码省略</span></span>
<span class="line"><span style="color:#A6ACCD;">  {/* 短信验证码输入框 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div className=&#39;input-item&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;Input</span></span>
<span class="line"><span style="color:#A6ACCD;">      className=&#39;input&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">      placeholder=&#39;请输入验证码&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">      autoComplete=&#39;off&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">      type=&#39;text&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">      name=&#39;code&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">      maxLength={6}</span></span>
<span class="line"><span style="color:#A6ACCD;">      onBlur={formik.handleBlur}</span></span>
<span class="line"><span style="color:#A6ACCD;">      errorMsg={formik.touched.code ? formik.errors.code : &#39;&#39;}</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">      extra={&lt;span&gt;获取验证码&lt;/span&gt;}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      extra={&lt;span onClick={() =&gt; handleSendCodes()}&gt;获取验证码&lt;/span&gt;}</span></span>
<span class="line"><span style="color:#A6ACCD;">      value={formik.values.code}</span></span>
<span class="line"><span style="color:#A6ACCD;">      onChange={formik.handleChange}</span></span>
<span class="line"><span style="color:#A6ACCD;">    /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br></div></div><h2 id="验证码倒计时功能" tabindex="-1">验证码倒计时功能 <a class="header-anchor" href="#验证码倒计时功能" aria-label="Permalink to &quot;验证码倒计时功能&quot;">​</a></h2><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">const dispatch = useDispatch()</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  const [time, setTime] = useState(0)</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  const handleSendCodes = async () =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // 倒计时未结束判断</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    if (time &gt; 0) return</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 校验错误判断</span></span>
<span class="line"><span style="color:#A6ACCD;">    if (formik.errors.mobile) return Toast.show({ content: formik.errors.mobile })</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 发送验证码</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">    dispatch(sendCodes(formik.values.mobile))</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    await dispatch(sendCodes(formik.values.mobile))</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // 成功提示</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    Toast.show({ content: &#39;获取验证码成功&#39;, duration: 1000 })</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // 开启倒计时</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    setTime(60)</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    let timerId = setInterval(() =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      // 💥当我们每次都想要获取到最新的状态，需要写成 箭头函数的形式</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      setTime((time) =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">        if (time === 1) clearInterval(timerId)</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">        return time - 1</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      })</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    }, 1000)</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">          {/* 短信验证码输入框 */}</span></span>
<span class="line"><span style="color:#A6ACCD;">          &lt;div className=&#39;input-item&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">            &lt;Input</span></span>
<span class="line"><span style="color:#A6ACCD;">              className=&#39;input&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              placeholder=&#39;请输入验证码&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              autoComplete=&#39;off&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              type=&#39;text&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              name=&#39;code&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">              maxLength={6}</span></span>
<span class="line"><span style="color:#A6ACCD;">              errMsg={formik.touched.code ? formik.errors.code : &#39;&#39;}</span></span>
<span class="line"><span style="color:#A6ACCD;">              onBlur={formik.handleBlur}</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">              extra={&lt;span&gt;获取验证码&lt;/span&gt;}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              extra={</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">                &lt;span onClick={handleSendCodes}&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">                  {time === 0 ? &#39;获取验证码&#39; : `${time}秒后重试`}</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">                &lt;/span&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">              }</span></span>
<span class="line"><span style="color:#A6ACCD;">              value={formik.values.code}</span></span>
<span class="line"><span style="color:#A6ACCD;">              onChange={formik.handleChange}</span></span>
<span class="line"><span style="color:#A6ACCD;">            /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">          &lt;/div&gt;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br></div></div><h2 id="定时器两个注意点" tabindex="-1">定时器两个注意点 <a class="header-anchor" href="#定时器两个注意点" aria-label="Permalink to &quot;定时器两个注意点&quot;">​</a></h2><h3 id="常见问题" tabindex="-1">常见问题 <a class="header-anchor" href="#常见问题" aria-label="Permalink to &quot;常见问题&quot;">​</a></h3><ol><li>定时器内部状态无法更新。</li><li>组件卸载没有清理定时器。</li></ol><h3 id="解决方案" tabindex="-1">解决方案 <a class="header-anchor" href="#解决方案" aria-label="Permalink to &quot;解决方案&quot;">​</a></h3><ol><li>更新状态改写成箭头函数写法。</li></ol><div class="language-tsx line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">tsx</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#82AAFF;">setTime</span><span style="color:#A6ACCD;">(</span><span style="color:#F78C6C;">60</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// ❌普通写法，定时器内无法获取最新 time 值更新</span></span>
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#A6ACCD;"> timerId </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">setInterval</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">定时器内部的time值</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">time</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#82AAFF;">setTime</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">time</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">-</span><span style="color:#F07178;"> </span><span style="color:#F78C6C;">1</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">},</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1000</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 🔔 箭头函数写法：每次都可获取到最新的状态</span></span>
<span class="line"><span style="color:#C792EA;">let</span><span style="color:#A6ACCD;"> timerId </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">setInterval</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#82AAFF;">setTime</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">time</span><span style="color:#89DDFF;">)</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">定时器内部的time值</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">time</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">time</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">-</span><span style="color:#F07178;"> </span><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">},</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">100</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><ol start="2"><li><code>useEffect</code> 清理定时器。</li></ol><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">const dispatch = useDispatch()</span></span>
<span class="line"><span style="color:#A6ACCD;">  const [time, setTime] = useState(0)</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  const timerId = useRef(-1)</span></span>
<span class="line"><span style="color:#A6ACCD;">  const handleSendCodes = async () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 倒计时没结束，直接退出</span></span>
<span class="line"><span style="color:#A6ACCD;">    if (time &gt; 0) return</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 检查手机号格式</span></span>
<span class="line"><span style="color:#A6ACCD;">    if (formik.errors.mobile) return Toast.show({ content: formik.errors.mobile })</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 🔔调用异步 action ，内部发送请求获取验证码</span></span>
<span class="line"><span style="color:#A6ACCD;">    await dispatch(sendCodes(formik.values.mobile))</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 成功提示</span></span>
<span class="line"><span style="color:#A6ACCD;">    Toast.show({ content: &#39;验证码发送成功&#39; })</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 开启倒计时</span></span>
<span class="line"><span style="color:#A6ACCD;">    setTime(60)</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">    let timerId = setInterval(() =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    timerId.current = window.setInterval(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 💥当我们每次都想要获取到最新的状态，需要写成 箭头函数 的形式</span></span>
<span class="line"><span style="color:#A6ACCD;">      setTime((time) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">        console.log(&#39;定时器内部的time值&#39;, time)</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">        if (time === 1) clearInterval(timerId)</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">        if (time === 1) clearInterval(timerId.current)</span></span>
<span class="line"><span style="color:#A6ACCD;">        return time - 1</span></span>
<span class="line"><span style="color:#A6ACCD;">      })</span></span>
<span class="line"><span style="color:#A6ACCD;">    }, 100)</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  useEffect(() =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // 清理函数</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    return () =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      // 组件卸载清理定时器，避免内存泄漏</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      clearInterval(timerId.current)</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    }</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  }, [])</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br></div></div><h3 id="useref-用法回顾" tabindex="-1"><code>useRef</code> 用法回顾 <a class="header-anchor" href="#useref-用法回顾" aria-label="Permalink to &quot;`useRef` 用法回顾&quot;">​</a></h3><p>作用 1：存储非页面渲染的可变数据，如：定时器 id。（PS：用于页面渲染的数据用 <code>useState</code> 定义）</p><p>作用 2：绑定 DOM 元素 或 组件元素，可用于调用 DOM 元素或组件的 属性和方法。</p><h2 id="登录并获取-token" tabindex="-1">登录并获取 Token <a class="header-anchor" href="#登录并获取-token" aria-label="Permalink to &quot;登录并获取 Token&quot;">​</a></h2><blockquote><p>目标：点击登录按钮后，发送表单数据到后端登录接口，获取登录凭证 Token</p></blockquote><p>实现思路：</p><ul><li>实现一个 Action，去调用后端登录接口</li><li>在 <code>formik</code> 的表单提交方法 <code>onSubmit</code> 中调用 Action</li></ul><p><strong>操作步骤</strong></p><ol><li>在 <code>src/store/action/user.ts</code>中，添加一个 Action 函数</li></ol><div class="language-js line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * 用户认证（登录注册）</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">object</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> { mobile, code }</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">returns</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> loginAction </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> (</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  mobile</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  code</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">}:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">mobile</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">string</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">code</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">string</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">) </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">async</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">dispatch</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#FFCB6B;">AppDispatch</span><span style="color:#89DDFF;">)</span><span style="color:#F07178;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#C792EA;">const</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">res</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;font-style:italic;">await</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">http</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">post</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">`</span><span style="color:#C3E88D;">/v1_0/authorizations</span><span style="color:#89DDFF;">`</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">mobile</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">code</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">`</span><span style="color:#C3E88D;">用户认证（登录注册）</span><span style="color:#89DDFF;">`</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">res</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">};</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br></div></div><ol start="2"><li>在登录页面组件中的 <code>formik</code> 表单对象的 <code>onSubmit</code> 方法中，调用 Action</li></ol><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">import { loginAction, sendCodes } from &#39;@/store/action/user&#39;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">const form = useFormik({</span></span>
<span class="line"><span style="color:#A6ACCD;">    // ...代码省略</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 提交表单</span></span>
<span class="line"><span style="color:#A6ACCD;">    onSubmit: (values) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">      console.log(&#39;当前表单的数据为&#39;, values)</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      dispatch(loginAction(values))</span></span>
<span class="line"><span style="color:#A6ACCD;">    },</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>如果能成功获取 Token 信息，控制台会打印出如下内容：</p><img src="/hm-docs/assets/image-20210901101009155.bbeafa00.png" alt="image-20210901101009155" style="zoom:50%;"><h2 id="提示消息优化" tabindex="-1">提示消息优化 <a class="header-anchor" href="#提示消息优化" aria-label="Permalink to &quot;提示消息优化&quot;">​</a></h2><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">	const navigate = useNavigate();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">  const formik = useFormik({</span></span>
<span class="line"><span style="color:#A6ACCD;">    // ...代码省略</span></span>
<span class="line"><span style="color:#A6ACCD;">	// 提交表单</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">    onSubmit: (values) =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">      dispatch(loginAction(values))</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    onSubmit: async (values) =&gt; {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      await dispatch(loginAction(values))</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      Toast.show({ content: &#39;登陆成功&#39; })</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      navigate(&#39;/&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">    },</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><h2 id="保存-token-到本地缓存" tabindex="-1">保存 Token 到本地缓存 <a class="header-anchor" href="#保存-token-到本地缓存" aria-label="Permalink to &quot;保存 Token 到本地缓存&quot;">​</a></h2><blockquote><p>目标：将从后端获取到的 Token 保存到浏览器的 <code>LocalStorage</code> 中</p></blockquote><p>实现思路：</p><ul><li>实现一个工具模块，在该模块中专门操作 <code>LocalStorage</code> 中的 <code>Token</code> 信息</li><li>在调用后端接口获取 <code>Token</code> 的 <code>Action</code> 中，调用该工具模块中的方法来存储 <code>Token</code></li></ul><p><strong>操作步骤</strong></p><ol><li>创建 <code>src/utils/storage.ts</code>，并编写 Token 的设置、获取、删除等工具方法</li></ol><div class="language-js line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#C792EA;">interface</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">IAuthType</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">token</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">string</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">refresh_token</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">string</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 用户 Token 的本地缓存键名</span></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> TOKEN_KEY </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">itcast-geek-park-h5</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * 从本地缓存中获取 Token 信息</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> getAuth </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">():</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">IAuthType</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">parse</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">localStorage</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">getItem</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">TOKEN_KEY</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">||</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">{}</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * 将 Token 信息存入缓存</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">Object</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">tokenInfo</span><span style="color:#676E95;font-style:italic;"> 从后端获取到的 Token 信息</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> saveAuth </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">tokenInfo</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">IAuthType</span><span style="color:#89DDFF;">):</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">void</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">localStorage</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">setItem</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">TOKEN_KEY</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">JSON</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">stringify</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">tokenInfo</span><span style="color:#F07178;">))</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * 删除本地缓存中的 Token 信息</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> removeAuth </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">():</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">void</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#A6ACCD;">localStorage</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">removeItem</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">TOKEN_KEY</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * 判断本地缓存中是否存在 Token 信息</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> hasToken </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">():</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">boolean</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#82AAFF;">Boolean</span><span style="color:#F07178;">(</span><span style="color:#82AAFF;">getTokenInfo</span><span style="color:#F07178;">()</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">token</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br></div></div><ol start="2"><li>原先调用后端接口获取 Token 的 Action 中，调用以上的本地缓存工具方法来保存 Token 信息</li></ol><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">// 为了方便管理请求接口，所有的请求都以异步 action 的方式进行封装</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">import request from &#39;@/utils/request&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { saveAuth } from &#39;@/utils/storage&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { AppDispatch } from &#39;..&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">/**</span></span>
<span class="line"><span style="color:#A6ACCD;"> * 用户认证（登录注册）</span></span>
<span class="line"><span style="color:#A6ACCD;"> * @param {object} { mobile, code }</span></span>
<span class="line"><span style="color:#A6ACCD;"> * @returns</span></span>
<span class="line"><span style="color:#A6ACCD;"> */</span></span>
<span class="line"><span style="color:#A6ACCD;">export const loginAction = ({ mobile, code }: { mobile: string; code: string }) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  return async (dispatch: AppDispatch) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">    const res = await request.post(`/v1_0/authorizations`, { mobile, code })</span></span>
<span class="line"><span style="color:#A6ACCD;">    // console.log(`用户认证（登录注册）`, res)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // 保存到 本地存储</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    saveAuth(res.data)</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div><ul><li>打开本地存储，检查结果</li></ul><img src="/hm-docs/assets/image-20210901104851802.95f9f6e7.png" alt="image-20210901104851802" style="zoom:50%;"><h2 id="请求拦截器添加-token" tabindex="-1">请求拦截器添加 Token <a class="header-anchor" href="#请求拦截器添加-token" aria-label="Permalink to &quot;请求拦截器添加 Token&quot;">​</a></h2><blockquote><p>目标：在发送请求时在请求头中携带上 Token 信息，以便在请求需要鉴权的后端接口时可以顺利调用</p></blockquote><p>实现思路：</p><ul><li>在 <code>axios</code> 请求拦截器中，读取保存在 <code>Redux</code> 或 <code>localStorage</code> 中的 <code>Token</code> 信息，并设置到请求头上</li></ul><p><strong>操作步骤</strong></p><ol><li>在 <code>src/utils/request.ts</code> 中，改造请求拦截器：</li></ol><div class="language-diff line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">diff</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">import { getTokenInfo } from &#39;./storage&#39;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">// 请求拦截器</span></span>
<span class="line"><span style="color:#A6ACCD;">request.interceptors.request.use(</span></span>
<span class="line"><span style="color:#89DDFF;">-</span><span style="color:#F07178;">  function (config) {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">  function (config: any) {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // 获取缓存中的 Token 信息</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    const { token } = getAuth()</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    // 设置请求头的 Authorization 字段</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    if (token) {</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">      config.headers.Authorization = `Bearer ${token}`</span></span>
<span class="line"><span style="color:#89DDFF;">+</span><span style="color:#C3E88D;">    }</span></span>
<span class="line"><span style="color:#A6ACCD;">    return config</span></span>
<span class="line"><span style="color:#A6ACCD;">  },</span></span>
<span class="line"><span style="color:#A6ACCD;">  function (error) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    return Promise.reject(error)</span></span>
<span class="line"><span style="color:#A6ACCD;">  },</span></span>
<span class="line"><span style="color:#A6ACCD;">)</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div></div></div></main><footer class="VPDocFooter" data-v-e9631fd0 data-v-face870a><!--[--><!--]--><div class="edit-info" data-v-face870a><!----><div class="last-updated" data-v-face870a><p class="VPLastUpdated" data-v-face870a data-v-149a99df>更新: <time datetime="2023-08-31T13:50:55.000Z" data-v-149a99df></time></p></div></div><!----></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><footer class="VPFooter" data-v-99cf8a88 data-v-f4388a15><div class="container" data-v-f4388a15><p class="message" data-v-f4388a15>Released under the MIT License.</p><p class="copyright" data-v-f4388a15>Copyright © 2022-present Shugang Zhou 传智教育</p></div></footer><!--[--><!--]--></div></div>
    <script>__VP_HASH_MAP__ = JSON.parse("{\"index.md\":\"d89339df\",\"electron_eletron.md\":\"f78b205d\",\"hmzs_前台可视化_04-部署上线说明.md\":\"b689f494\",\"geek-park_03-布局容器.md\":\"f9a95023\",\"hmzs_前台可视化_03-接入微前端.md\":\"e4e73fe1\",\"geek-park_index.md\":\"274ee860\",\"hr_day01-项目介绍及初始化-登录页面.md\":\"a87e2d0f\",\"hmzs_前台可视化_01-项目环境搭建.md\":\"7fa65aa4\",\"hr_day02-登录模块-主页鉴权.md\":\"7c74ca95\",\"geek-park_02-登录页面.md\":\"6ab1a2cf\",\"memory.md\":\"22cd8c53\",\"mini-rabbit_01-项目.md\":\"3c3d7316\",\"geek-park_01-项目准备.md\":\"23a5f83b\",\"hr_day10-首页图表-nginx上线.md\":\"8a2d49ab\",\"other-resource_index.md\":\"e64d1d24\",\"mini-rabbit_04-分类.md\":\"26f22ed4\",\"interview_面经pc端02.md\":\"88e355b4\",\"hr_day05-组织架构-角色管理.md\":\"bef256c2\",\"hr_day08-cos上传和权限数据.md\":\"c03b8bcb\",\"hr_day03-主页模块-修改密码.md\":\"bc380dd0\",\"mini-rabbit_index.md\":\"9e57a010\",\"hr_day04-组织架构.md\":\"e838901c\",\"hmzs_前台可视化_02-大屏可视化.md\":\"98a26e50\",\"mini-rabbit_09-购物车.md\":\"0a823f26\",\"pinia_index.md\":\"e132c8fe\",\"ts_pro.md\":\"fe9a6545\",\"hr_day06-角色管理-员工管理.md\":\"beb02c6b\",\"mini-rabbit_03-推荐.md\":\"faed0355\",\"mini-rabbit_06-地址.md\":\"1fc49004\",\"interview_面经h5端02.md\":\"a7342342\",\"mini-rabbit_08-sku.md\":\"0775a2c7\",\"ts_backup.md\":\"55f33e6d\",\"hr_day07-员工管理-上传下载.md\":\"dea880ea\",\"vue2_base.md\":\"b804c94a\",\"project_super-doctor.md\":\"f0b1af73\",\"interview_面经h5端01.md\":\"e9040838\",\"geek-park_04-个人中心.md\":\"f16a09df\",\"ts_case.md\":\"7cd17957\",\"vue3_index.md\":\"5e17a031\",\"interview_面经pc端01.md\":\"d7141b56\",\"ts_index.md\":\"a01dfcff\",\"vue3_case.md\":\"d48addba\",\"project_login.md\":\"c54c8b93\",\"project_index.md\":\"6674c051\",\"mini-rabbit_02-首页.md\":\"299b4f8e\",\"project_end.md\":\"cb143198\",\"vue2_vuex.md\":\"a087fc79\",\"ts_advanced.md\":\"319200c0\",\"ts_core.md\":\"fb357910\",\"project_home.md\":\"86083086\",\"project_user.md\":\"c888dc36\",\"vue3_composition.md\":\"72ec4356\",\"mini-rabbit_07-详情.md\":\"1fe593a3\",\"mini-rabbit_10-订单.md\":\"732bea0a\",\"project_consult.md\":\"44bd936e\",\"project_room.md\":\"e43da8e9\",\"mini-rabbit_05-登录.md\":\"0cae2bde\",\"project_order.md\":\"8793ea0c\",\"project_consult-order.md\":\"8f1e59fa\",\"hr_day09-权限应用-首页.md\":\"4ca0b809\"}")
__VP_SITE_DATA__ = JSON.parse("{\"lang\":\"zh-CN\",\"dir\":\"ltr\",\"title\":\"优医问诊H5\",\"description\":\"最新Vue3技术栈,Vue3,TS,Pinia,Vant,在线问诊项目,H5\",\"base\":\"/hm-docs/\",\"head\":[],\"appearance\":true,\"themeConfig\":{\"logo\":\"/logo2.png\",\"search\":{\"provider\":\"local\"},\"nav\":[{\"text\":\"vue2\",\"link\":\"/vue2/base\"},{\"text\":\"vuex\",\"link\":\"/vue2/vuex\"},{\"text\":\"面经\",\"link\":\"/interview/面经H5端01\"},{\"text\":\"人资\",\"link\":\"/hr/Day01-项目介绍及初始化-登录页面\"},{\"text\":\"黑马智数\",\"link\":\"/hmzs/前台可视化/01-项目环境搭建\"},{\"text\":\"小兔鲜儿\",\"link\":\"/mini-rabbit/\"},{\"text\":\"TypeScript\",\"link\":\"/ts/\"},{\"text\":\"Vue3\",\"link\":\"/vue3/\"},{\"text\":\"Pinia\",\"link\":\"/pinia/\"},{\"text\":\"优医问诊\",\"link\":\"/project/\"},{\"text\":\"其它\",\"link\":\"/other-resource/\"}],\"sidebar\":{\"/vu2/\":[{\"text\":\"vue基础\",\"items\":[{\"text\":\"vue基础\",\"link\":\"/vu2/base\"}]}],\"/interview/\":[{\"text\":\"面经H5\",\"items\":[{\"text\":\"面经H5端-（上）\",\"link\":\"/interview/面经H5端01\"},{\"text\":\"面经H5端-（下）\",\"link\":\"/interview/面经H5端02\"},{\"text\":\"面经PC端-（上）\",\"link\":\"/interview/面经PC端01\"},{\"text\":\"面经PC端-（下）\",\"link\":\"/interview/面经PC端02\"}]}],\"/hr/\":[{\"text\":\"面经H5\",\"items\":[{\"text\":\"Day01\",\"link\":\"/hr/Day01-项目介绍及初始化-登录页面\"},{\"text\":\"Day02\",\"link\":\"/hr/Day02-登录模块-主页鉴权\"},{\"text\":\"Day03\",\"link\":\"/hr/Day03-主页模块-修改密码\"},{\"text\":\"Day04\",\"link\":\"/hr/Day04-组织架构\"},{\"text\":\"Day05\",\"link\":\"/hr/Day05-组织架构-角色管理\"},{\"text\":\"Day06\",\"link\":\"/hr/Day06-角色管理-员工管理\"},{\"text\":\"Day07\",\"link\":\"/hr/Day07-员工管理-上传下载\"},{\"text\":\"Day08\",\"link\":\"/hr/Day08-Cos上传和权限数据\"},{\"text\":\"Day09\",\"link\":\"/hr/Day09-权限应用-首页\"},{\"text\":\"Day10\",\"link\":\"/hr/Day10-首页图表-Nginx上线\"}]}],\"/hmzs/\":[{\"text\":\"黑马智数\",\"items\":[{\"text\":\"初始化项目\",\"link\":\"/hmzs/前台可视化/01-项目环境搭建\"},{\"text\":\"大屏可视化\",\"link\":\"/hmzs/前台可视化/02-大屏可视化\"},{\"text\":\"接入微前端\",\"link\":\"/hmzs/前台可视化/03-接入微前端\"},{\"text\":\"部署上线\",\"link\":\"/hmzs/前台可视化/04-部署上线说明\"}]}],\"/mini-rabbit/\":[{\"text\":\"小兔仙\",\"items\":[{\"link\":\"/mini-rabbit/01-项目\",\"text\":\"01-项目准备\"},{\"link\":\"/mini-rabbit/02-首页\",\"text\":\"02-首页模块\"},{\"link\":\"/mini-rabbit/03-推荐\",\"text\":\"03-推荐模块\"},{\"link\":\"/mini-rabbit/04-分类\",\"text\":\"04-分类模块\"},{\"link\":\"/mini-rabbit/05-登录\",\"text\":\"05-登录模块\"},{\"link\":\"/mini-rabbit/06-地址\",\"text\":\"06-地址模块\"},{\"link\":\"/mini-rabbit/07-详情\",\"text\":\"07-详情模块\"},{\"link\":\"/mini-rabbit/08-SKU\",\"text\":\"08-SKU模块\"},{\"link\":\"/mini-rabbit/09-购物车\",\"text\":\"09-购物车模块\"},{\"link\":\"/mini-rabbit/10-订单\",\"text\":\"10-订单模块\"}]}],\"/memory/\":[{\"text\":\"需要记忆的内容\",\"items\":[{\"link\":\"/memory/\",\"text\":\"需要记忆的内容\"}]}],\"/ts/\":[{\"text\":\"TypeScript\",\"items\":[{\"text\":\"TypeScript起步\",\"link\":\"/ts/\"},{\"text\":\"TypeScript核心\",\"link\":\"/ts/core\"},{\"text\":\"TypeScript进阶\",\"link\":\"/ts/advanced\"},{\"text\":\"TypeScript应用\",\"link\":\"/ts/pro\"},{\"text\":\"TS黑马头条案例\",\"link\":\"/ts/case\"}]}],\"/vue3/\":[{\"text\":\"Vue3核心\",\"items\":[{\"text\":\"快速开始\",\"link\":\"/vue3/\"},{\"text\":\"组合式API\",\"link\":\"/vue3/composition\"},{\"text\":\"综合案例\",\"link\":\"/vue3/case\"}]}],\"/pinia/\":[{\"text\":\"Pinia\",\"items\":[{\"text\":\"Pinia 核心\",\"link\":\"/pinia/\"}]}],\"/project/\":[{\"text\":\"优医问诊H5项目课程\",\"items\":[{\"text\":\"1. 项目起步\",\"link\":\"/project/\"},{\"text\":\"2. 登录模块\",\"link\":\"/project/login\"},{\"text\":\"3. 用户模块\",\"link\":\"/project/user\"},{\"text\":\"4. 首页模块\",\"link\":\"/project/home\"},{\"text\":\"5. 极速问诊\",\"link\":\"/project/consult\"},{\"text\":\"6. 医生问诊室\",\"link\":\"/project/room\"},{\"text\":\"7. 问诊订单模块\",\"link\":\"/project/consult-order\"},{\"text\":\"8. 药品订单模块\",\"link\":\"/project/order\"},{\"text\":\"9. 其他扩展\",\"link\":\"/project/end\"},{\"text\":\"辅助-超级医生\",\"link\":\"/project/super-doctor\"}]}]},\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://gitee.com/luckybo0027\"}],\"footer\":{\"message\":\"Released under the MIT License.\",\"copyright\":\"Copyright © 2022-present Shugang Zhou 传智教育\"},\"lastUpdatedText\":\"更新\",\"outline\":\"deep\"},\"locales\":{},\"scrollOffset\":90,\"cleanUrls\":false}")</script>
    
  </body>
</html>